﻿namespace OpcDaNetClient
{
    public partial class Form_OpcDaNetClient : Form
    {
        public Form_OpcDaNetClient()
        {
            InitializeComponent();
        }

        private OpcDaNet.Da.Server m_OpcServer = null;

        // members to store configuration
        private OpcDaNetClient.Form_AddItems m_DialogAddItems = null;
        private InternalStateControl m_InternalStateControl;

        //
        // constant values
        private readonly String BUTTON_DESCRIPTION_SERVER_CONNECT = "Connect";
        private readonly String BUTTON_DESCRIPTION_SERVER_DISCONNECT = "Disconnect";
        private readonly String BUTTON_DESCRIPTION_GROUP_ADD = "Add";
        private readonly String BUTTON_DESCRIPTION_GROUP_REMOVE = "Remove";
        private readonly Int32 MAX_ITEMS = 10;

        // Step: 2 up to 18 (exclude Step: 13 / 16)
        //
        // opc server command and states
        #region " Form Events "

        // Step: 2
        //
        // browse for available opc servers on this system
        private void Form_OpcDaNetClient_Load(object sender, EventArgs e)
        {
            try
            {
                // Step: 2
                //
                // browse for available opc servers on this system
                OpcDaNet.IDiscovery opcDiscovery = new OpcDaNet.Com.ServerEnumerator();
                OpcDaNet.Server[] opcServers = opcDiscovery.GetAvailableServers(
                    OpcDaNet.Specification.COM_DA_20);

                // copy opc server to ...
                this.ComboBox_Server.BeginUpdate();

                foreach (OpcDaNet.Server opcServer in opcServers)
                {
                    this.ComboBox_Server.Items.Add(opcServer.Url.Path);
                }
            }
            catch (Exception except)
            {
                this.ShowExceptionInfo(except);
            }
            finally
            {
                this.ComboBox_Server.EndUpdate();

                // set sync as default access
                this.RadioButton_Sync.Checked = true;

                // Enable or disable controls
                this.SetInternalStateControl(InternalStateControl.Disconnected);
            }
        }

        private void Form_OpcDaNetClient_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (this.m_InternalStateControl != InternalStateControl.Disconnected)
            {
                e.Cancel = true;
                MessageBox.Show("Please remove the Items and the Subscription to close Application!",
                     this.Text, MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
        }

        // Step: 3 / 4
        //
        // connect and disconnet to/from opc server
        private void Button_Connect_Disconnect_Click(object sender, EventArgs e)
        {
            try
            {
                // show hour glas
                this.Cursor = Cursors.WaitCursor;

                if (this.Button_Connect_Disconnect.Text.Equals(BUTTON_DESCRIPTION_SERVER_CONNECT))
                {
                    // Step: 3
                    //
                    // select opc server and
                    this.m_OpcServer = new OpcDaNet.Da.Server(new OpcDaNet.Com.Factory(),
                        new OpcDaNet.URL(OpcDaNet.UrlScheme.DA + "://localhost/" +
                        this.ComboBox_Server.Text));

                    // connect with ...
                    this.m_OpcServer.Connect();

                    // Enable or disable controls
                    this.ComboBox_Server.Focus();
                    this.SetInternalStateControl(InternalStateControl.Connected);
                }
                else if (this.Button_Connect_Disconnect.Text.Equals(BUTTON_DESCRIPTION_SERVER_DISCONNECT))
                {
                    // Step: 4
                    //
                    // disconnect from server and clean up
                    this.m_OpcServer.Disconnect();
                    this.m_OpcServer.Dispose();
                    this.m_OpcServer = null;

                    // Enable or disable controls
                    this.SetInternalStateControl(InternalStateControl.Disconnected);

                    // clear browse info
                    if (this.m_DialogAddItems is Form_AddItems)
                        this.m_DialogAddItems.Clear();
                }
            }
            catch (Exception except)
            {
                this.ShowExceptionInfo(except);
            }
            finally
            {
                // restore cursor
                this.Cursor = Cursors.Default;
            }
        }

        // Step: 5
        //
        // show server info dialog
        private void Button_ServerInfo_Click(object sender, EventArgs e)
        {
            try
            {
                // show hour glas
                this.Cursor = Cursors.WaitCursor;

                //
                //check precondition to show group information
                Debug.Assert(this.m_OpcServer != null);
                if (this.m_OpcServer == null)
                    return;

                // Step: 5
                //
                // show server info dialog
                OpcDaNetClient.Form_ServerInfo form;
                form = new OpcDaNetClient.Form_ServerInfo();
                form.OPCServer = this.m_OpcServer;
                form.ShowDialog();
            }
            catch (Exception except)
            {
                this.ShowExceptionInfo(except);
            }
            finally
            {
                // restore cursor
                this.Cursor = Cursors.Default;
            }
        }

        // Step: 6 / 7
        //
        // create and cancel subscription
        private void Button_Add_Remove_Group_Click(object sender, EventArgs e)
        {
            try
            {
                // show hour glas
                this.Cursor = Cursors.WaitCursor;

                if (this.Button_Add_Remove_Group.Text.Equals(BUTTON_DESCRIPTION_GROUP_ADD))
                {
                    // Step: 6
                    //
                    // create subscription
                    OpcDaNet.Da.SubscriptionState subScriptionState = new OpcDaNet.Da.SubscriptionState();
                    subScriptionState.Active = true;    // activate cyclic communication
                    subScriptionState.ClientHandle = System.Guid.NewGuid().ToString();
                    subScriptionState.Deadband = 0.0F;
                    subScriptionState.Name = TextBox_Group.Text;
                    subScriptionState.UpdateRate = 1000;

                    this.m_OpcServer.CreateSubscription(subScriptionState);

                    // Enable or disable controls
                    this.SetInternalStateControl(InternalStateControl.SubscriptionSet);
                }
                else if (this.Button_Add_Remove_Group.Text.Equals(BUTTON_DESCRIPTION_GROUP_REMOVE))
                {
                    // Step: 7
                    //
                    // cancel subscription and clean up
                    OpcDaNet.Da.ISubscription iSubscription = this.m_OpcServer.Subscriptions[0];
                    this.m_OpcServer.CancelSubscription(iSubscription);
                    iSubscription.Dispose();
                    iSubscription = null;

                    // Enable or disable controls
                    this.SetInternalStateControl(InternalStateControl.Connected);
                }
            }
            catch (Exception except)
            {
                this.ShowExceptionInfo(except);
            }
            finally
            {
                // restore cursor
                this.Cursor = Cursors.Default;
            }
        }

        // Step: 8
        //
        // show group info dialog
        private void Button_GroupInfo_Click(object sender, EventArgs e)
        {
            try
            {
                // show hour glas
                this.Cursor = Cursors.WaitCursor;

                // check precondition to show group information
                Debug.Assert(this.m_OpcServer != null);
                if (this.m_OpcServer == null)
                    return;

                // Step: 8
                //
                // show group info dialog
                OpcDaNetClient.Form_GroupInfo form = new OpcDaNetClient.Form_GroupInfo();
                form.OPCServer = this.m_OpcServer;
                form.ShowDialog();
            }
            catch (Exception except)
            {
                this.ShowExceptionInfo(except);
            }
            finally
            {
                // restore cursor
                this.Cursor = Cursors.Default;
            }
        }

        // Step: 9
        //
        // add items to the subscription
        private void Button_Add_Item_Click(object sender, EventArgs e)
        {
            try
            {
                // show hour glas
                this.Cursor = Cursors.WaitCursor;

                // check precondition to show group information
                Debug.Assert(this.m_OpcServer != null);
                if (this.m_OpcServer == null)
                    return;
                Debug.Assert(this.m_OpcServer.Subscriptions.Count > 0);
                if (this.m_OpcServer.Subscriptions.Count == 0)
                    return;

                // show dialog to select items
                if (m_DialogAddItems == null)
                    m_DialogAddItems = new OpcDaNetClient.Form_AddItems();
                m_DialogAddItems.OPCServer = this.m_OpcServer;
                m_DialogAddItems.MaxItems = MAX_ITEMS - this.ListView_ItemData.Items.Count;
                m_DialogAddItems.SeparatorSign = ";";

                DialogResult dlgResult = m_DialogAddItems.ShowDialog();
                if (dlgResult != System.Windows.Forms.DialogResult.OK)
                    return;

                // no item selected?
                if ((m_DialogAddItems.ItemName == null) ||
                     (m_DialogAddItems.ItemName.Length == 0))
                    return;

                // prepare multiple selected items
                String[] items = this.m_DialogAddItems.ItemName.Split(
                    m_DialogAddItems.SeparatorSign.ToCharArray());

                // Step: 9
                //
                // add items to the subscription
                OpcDaNet.Da.Item[] opcItems = new OpcDaNet.Da.Item[items.Length];
                for (Int32 index = 0; index < items.Length; index++)
                {
                    // Step: 9.1
                    //
                    // creating new item
                    opcItems[index] = new OpcDaNet.Da.Item();
                    // set item properties
                    opcItems[index].ClientHandle = index;
                    opcItems[index].Active = true;
                    opcItems[index].ActiveSpecified = true;
                    opcItems[index].ItemName = items[index];
                    opcItems[index].ItemPath = "";
                    opcItems[index].MaxAge = -1;
                    //opcItems[index].MaxAgeSpecified = false;	// read from device
                    opcItems[index].MaxAgeSpecified = true;     // read from cache
                }

                // Step: 9.2
                //
                // add the item to the subscription and to the list view
                ListViewItem listViewItem;
                OpcDaNet.Da.ItemResult[] itemResults = this.m_OpcServer.Subscriptions[0].AddItems(opcItems);
                for (Int32 index = 0; index < itemResults.Length; index++)
                {
                    if (itemResults[index].ResultID.Succeeded() == true)
                    {
                        listViewItem = this.ListView_ItemData.Items.Add(opcItems[index].ItemName);
                        listViewItem.SubItems.Add(""); // sub item 'Value'
                        listViewItem.SubItems.Add(""); // sub item 'Quality'
                    }
                }

                // enable or disable controls
                if (this.ListView_ItemData.Items.Count > 0)
                    this.SetInternalStateControl(InternalStateControl.ItemsSet);
            }
            catch (Exception except)
            {
                this.ShowExceptionInfo(except);
            }
            finally
            {
                // restore cursor
                this.Cursor = Cursors.Default;
            }
        }

        // Step: 10
        //
        // remove items from the subscription
        private void Button_Remove_Item_Click(object sender, EventArgs e)
        {
            try
            {
                // show hour glas
                this.Cursor = Cursors.WaitCursor;

                // check precondition to show group information
                Debug.Assert(this.m_OpcServer != null);
                if (this.m_OpcServer == null)
                    return;
                Debug.Assert(this.m_OpcServer.Subscriptions.Count > 0);
                if (this.m_OpcServer.Subscriptions.Count == 0)
                    return;

                // any item seleceted?
                if (this.ListView_ItemData.SelectedItems.Count <= 0)
                    return;

                // disable cyclic communication
                this.RadioButton_Sync.Checked = true;

                // set array size for opc item list
                Int32 selectedItems = this.ListView_ItemData.SelectedItems.Count;
                OpcDaNet.ItemIdentifier[] opcItemIdents = new OpcDaNet.ItemIdentifier[selectedItems];

                // Step: 10
                //
                // remove selected items from opc subscription and from the list
                for (Int32 index = 0; index < selectedItems; index++)
                {
                    foreach (OpcDaNet.Da.Item opcItem in this.m_OpcServer.Subscriptions[0].Items)
                    {
                        if (this.ListView_ItemData.SelectedItems[index].Text.Equals(opcItem.ItemName))
                        {
                            opcItemIdents[index] = opcItem;
                            break;
                        }
                    }
                }
                // remove items
                OpcDaNet.IdentifiedResult[] opcIdentResult =
                    this.m_OpcServer.Subscriptions[0].RemoveItems(opcItemIdents);

                // clean up list view control
                for (Int32 index = 0; index < opcIdentResult.Length; index++)
                {
                    if (opcIdentResult[index].ResultID == ResultID.S_OK)
                        foreach (ListViewItem listViewItem in this.ListView_ItemData.SelectedItems)
                            if (opcIdentResult[index].ItemName.Equals(listViewItem.Text))
                            {
                                this.ListView_ItemData.Items.Remove(listViewItem);
                                break;
                            }
                }

                // Enable or disable controls
                if (this.ListView_ItemData.Items.Count <= 0)
                    this.SetInternalStateControl(InternalStateControl.SubscriptionSet);
            }
            catch (Exception except)
            {
                this.ShowExceptionInfo(except);
            }
            finally
            {
                // restore cursor
                this.Cursor = Cursors.Default;
            }
        }

        // Step: 11 / 12
        //
        // synchronous and asynchronous read of data
        private void Button_Read_Click(object sender, EventArgs e)
        {
            try
            {
                // show hour glas
                this.Cursor = Cursors.WaitCursor;

                // check precondition to show group information
                Debug.Assert(this.m_OpcServer != null);
                if (this.m_OpcServer == null)
                    return;
                Debug.Assert(this.m_OpcServer.Subscriptions.Count > 0);
                if (this.m_OpcServer.Subscriptions.Count == 0)
                    return;

                // Enable or disable controls
                this.SetReadWriteState(false);

                // check command type - sync or async
                if (this.RadioButton_Sync.Checked == true)
                {
                    // Step: 11
                    //
                    // synchronous read of data
                    OpcDaNet.Da.ItemValueResult[] opcValueResults = this.m_OpcServer.Subscriptions[0].Read(
                        this.m_OpcServer.Subscriptions[0].Items);
                    this.OnReadComplete(null, opcValueResults);
                }
                else if (this.RadioButton_Async.Checked == true)
                {
                    // Step: 12
                    //
                    // asynchronous read of data
                    OpcDaNet.IRequest opcRequest;
                    this.m_OpcServer.Subscriptions[0].Read(this.m_OpcServer.Subscriptions[0].Items,
                        null, new OpcDaNet.Da.ReadCompleteEventHandler(OnReadComplete), out opcRequest);
                }
            }
            catch (Exception except)
            {
                this.ShowExceptionInfo(except);
            }
            finally
            {
                // restore cursor
                this.Cursor = Cursors.Default;
            }
        }

        // Step: 14 / 15
        //
        // synchronous and asynchronous write of data
        private void Button_Write_Click(object sender, EventArgs e)
        {
            try
            {
                // show hour glas
                this.Cursor = Cursors.WaitCursor;

                // check precondition to show group information
                Debug.Assert(this.m_OpcServer != null);
                if (this.m_OpcServer == null)
                    return;
                Debug.Assert(this.m_OpcServer.Subscriptions.Count > 0);
                if (this.m_OpcServer.Subscriptions.Count == 0)
                    return;

                // check for selected item
                if (this.ListView_ItemData.SelectedItems.Count < 1)
                {
                    MessageBox.Show("No Item is selected!\r\nPlease select only one Item!",
                        this.Text, MessageBoxButtons.OK, MessageBoxIcon.Information);
                    return;
                }
                if (this.ListView_ItemData.SelectedItems.Count > 1)
                {
                    MessageBox.Show("Multiple Items are selected!\r\nPlease select only one Item!",
                        this.Text, MessageBoxButtons.OK, MessageBoxIcon.Information);
                    return;
                }

                // Enable or disable controls
                this.SetReadWriteState(false);

                // search for item
                OpcDaNet.Da.ItemValue[] opcItemValues = new OpcDaNet.Da.ItemValue[1];
                foreach (OpcDaNet.Da.Item opcItem in this.m_OpcServer.Subscriptions[0].Items)
                {
                    if (opcItem.ItemName.Equals(this.ListView_ItemData.SelectedItems[0].Text))
                    {
                        opcItemValues[0] = new OpcDaNet.Da.ItemValue(opcItem);
                        break;
                    }
                }
                if (opcItemValues == null)
                    return;

                // retrieve data type information
                ItemPropertyCollection[] itemPropertyCollection =
                    m_OpcServer.GetProperties(opcItemValues,
                    new PropertyID[] { Property.DATATYPE }, true);
                System.Type itemType = itemPropertyCollection[0][0].Value as System.Type;

                if (!itemType.IsArray)
                    opcItemValues[0].Value = this.TextBox_ItemValue.Text;
                else
                    opcItemValues[0].Value = this.TextBox_ItemValue.Text.Split(new Char[] { ' ' });


                // check command type - sync or async
                if (this.RadioButton_Sync.Checked == true)
                {
                    // Step: 14
                    //
                    // synchronous write
                    OpcDaNet.IdentifiedResult[] opcIdentResults =
                        this.m_OpcServer.Subscriptions[0].Write(opcItemValues);
                    this.OnWriteComplete(null, opcIdentResults);
                }
                else if (this.RadioButton_Async.Checked == true)
                {
                    // Step: 15
                    //
                    // asynchronous write
                    OpcDaNet.IRequest opcRequest;
                    this.m_OpcServer.Subscriptions[0].Write(opcItemValues, null,
                        new OpcDaNet.Da.WriteCompleteEventHandler(OnWriteComplete), out opcRequest);
                }
            }
            catch (Exception except)
            {
                this.ShowExceptionInfo(except);
            }
            finally
            {
                // restore cursor
                this.Cursor = Cursors.Default;
            }
        }

        // Step: 17 / 18
        //
        // add and remove event handler for cyclic communication
        private void RadioButton_CheckedChanged(object sender, EventArgs e)
        {
            try
            {
                // show hour glas
                this.Cursor = Cursors.WaitCursor;

                // check precondition to show group information
                if (this.m_OpcServer == null)
                    return;
                if (this.m_OpcServer.Subscriptions.Count == 0)
                    return;

                // cast to radio button
                RadioButton radioBtn = sender as RadioButton;

                if (radioBtn.Text.Equals(this.RadioButton_Cyclic.Text))
                {
                    OpcDaNet.Da.ISubscription iSubscription;
                    if (this.RadioButton_Cyclic.Checked == true)
                    {
                        // Step: 17
                        //
                        // add event handler for cyclic communication
                        iSubscription = this.m_OpcServer.Subscriptions[0];
                        iSubscription.DataChanged += new OpcDaNet.Da.DataChangedEventHandler(DataChanged);
                    }
                    else
                    {
                        // Step: 18
                        //
                        // remove event handler for cyclic communication
                        iSubscription = this.m_OpcServer.Subscriptions[0];
                        iSubscription.DataChanged -= new OpcDaNet.Da.DataChangedEventHandler(DataChanged);
                    }
                }
                // Enable or disable controls
                this.SetInternalStateItemData();
            }
            catch (Exception except)
            {
                this.ShowExceptionInfo(except);
            }
            finally
            {
                // restore cursor
                this.Cursor = Cursors.Default;
            }
        }

        //
        //
        // show item values in the Edit Field 'TextBox_ItemValue'
        private void ListView_ItemData_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (this.ListView_ItemData.SelectedItems.Count == 1)
                this.TextBox_ItemValue.Text = this.ListView_ItemData.SelectedItems[0].SubItems[1].Text;
            else
                this.TextBox_ItemValue.Text = "";
        }


        #endregion


        // Step: 13 / 16 / 19
        //
        // opc server events
        #region " OPC Events "

        // Step: 13
        //
        // This method is a delegate, which will be executed, if the 
        // asynchronous read is finished
        private void OnReadComplete(Object clientHandle, OpcDaNet.Da.ItemValueResult[] opcValueResults)
        {
            try
            {
                // Step: 13.1
                //
                // check if it has to be synchronized with the thread of the form 
                if (this.InvokeRequired == true)
                {
                    this.BeginInvoke(new OpcDaNet.Da.ReadCompleteEventHandler(OnReadComplete),
                        new Object[] { clientHandle, opcValueResults });
                    return;
                }

                // stop updating
                this.ListView_ItemData.BeginUpdate();

                // Step: 13.2
                //
                // update item values
                ListViewItem listViewItem;
                foreach (OpcDaNet.Da.ItemValueResult opcValueResult in opcValueResults)
                {
                    listViewItem = this.ListView_ItemData.FindItemWithText(opcValueResult.ItemName);

                    if ((listViewItem != null) && (opcValueResult.Value != null))
                    {
                        listViewItem.SubItems[1].Text = this.GetValueAsString(opcValueResult);
                        listViewItem.SubItems[2].Text = opcValueResult.Quality.ToString();
                    }
                }
            }
            catch (Exception except)
            {
                this.ShowExceptionInfo(except);
            }
            finally
            {
                if (this.InvokeRequired == false)
                {
                    // restart updating
                    this.ListView_ItemData.EndUpdate();

                    // Enable or disable controls
                    this.SetReadWriteState(true);
                }
            }
        }

        // Step: 16
        //
        // This method is a delegate, which will be executed, if the 
        // asynchronous write is finished
        private void OnWriteComplete(Object clientHandle, OpcDaNet.IdentifiedResult[] opcIdentResults)
        {
            try
            {
                // Step: 16.1
                //
                //Check if it has to be synchronized with the thread of the form 
                if (this.InvokeRequired == true)
                {
                    this.BeginInvoke(new OpcDaNet.Da.WriteCompleteEventHandler(OnWriteComplete),
                        new Object[] { clientHandle, opcIdentResults });
                    return;
                }

                // Step: 16.2
                //
                // show result
                Boolean error = false;
                StringBuilder errorInfo = new StringBuilder();
                errorInfo.Remove(0, errorInfo.Length);
                errorInfo.AppendLine("The following variables could not be written in case of error!");
                foreach (OpcDaNet.IdentifiedResult opcIdentResult in opcIdentResults)
                {
                    if (opcIdentResult.ResultID != OpcDaNet.ResultID.S_OK)
                    {
                        error = true;
                        errorInfo.AppendFormat("Item Name: {0}{1}Error Info: {2}{3}Error Number: {4}{5}",
                            opcIdentResult.ItemName, Environment.NewLine,
                            opcIdentResult.DiagnosticInfo, Environment.NewLine,
                            opcIdentResult.ResultID, Environment.NewLine);
                    }
                }
                if (error)
                    MessageBox.Show(errorInfo.ToString(), "Error while writing variables",
                        MessageBoxButtons.OK, MessageBoxIcon.Error);
                // opcIdentResults[0].ResultID.ToString()
            }
            catch (Exception except)
            {
                this.ShowExceptionInfo(except);
            }
            finally
            {
                if (this.InvokeRequired == false)
                {
                    // Enable or disable controls
                    this.SetReadWriteState(true);
                }
            }
        }

        // Step: 19
        //
        // event handler to receive callbacks from opc server during cyclic
        // read of data
        private void DataChanged(Object subscriptionHandle, Object requestHandle,
            OpcDaNet.Da.ItemValueResult[] opcItemValueResults)
        {
            try
            {
                // Step: 19.1
                //
                // check if it has to be synchronized with the form thread
                if (this.InvokeRequired == true)
                {
                    this.BeginInvoke(new OpcDaNet.Da.DataChangedEventHandler(DataChanged),
                        new Object[] { subscriptionHandle, requestHandle, opcItemValueResults });
                    return;
                }

                // stop updating
                this.ListView_ItemData.BeginUpdate();

                // Step: 19.2
                //
                // update item values
                this.OnReadComplete(null, opcItemValueResults);

            }
            catch (Exception except)
            {
                this.ShowExceptionInfo(except);
            }
            finally
            {
                // restart updating
                if (this.InvokeRequired == false)
                    this.ListView_ItemData.EndUpdate();
            }
        }

        #endregion
        #region " Additional Internal Functions "

        private String GetValueAsString(OpcDaNet.Da.ItemValueResult opcValueResult)
        {
            StringBuilder valueString = new StringBuilder();

            if ((opcValueResult != null) && (opcValueResult.Value != null))
            {
                if (!opcValueResult.Value.GetType().IsArray)
                {
                    // OpcDaNet.Da.ItemValueResult is no Array
                    return opcValueResult.Value.ToString();
                }
                else
                {
                    // OpcDaNet.Da.ItemValueResult is an Array
                    valueString.Remove(0, valueString.Length);

                    System.Collections.IList iList = opcValueResult.Value as System.Collections.IList;
                    if (iList != null)
                        foreach (Object obj in iList)
                            valueString.AppendFormat("{0} ", obj);

                    return valueString.ToString(0, valueString.Length - 1);
                }
            }
            return String.Empty;
        }

        private enum InternalStateControl
        {
            Disconnected = 0,       // Initialized
            Connected = 1,          // no subscription set
            SubscriptionSet = 2,    // items cleared
            ItemsSet = 3
        }

        private void SetInternalStateControl(InternalStateControl newState)
        {
            Boolean state;

            //store internal state
            this.m_InternalStateControl = newState;

            //only enabled if no connection is established
            this.ComboBox_Server.Enabled = (newState == InternalStateControl.Disconnected);

            //always enabled, only description changed
            state = (newState <= InternalStateControl.Connected);
            this.Button_Connect_Disconnect.Enabled = state;
            if (newState == InternalStateControl.Disconnected)
                this.Button_Connect_Disconnect.Text = BUTTON_DESCRIPTION_SERVER_CONNECT;
            else
                this.Button_Connect_Disconnect.Text = BUTTON_DESCRIPTION_SERVER_DISCONNECT;

            //enabled if an connection is established
            this.Button_ServerInfo.Enabled = (newState >= InternalStateControl.Connected);

            //enabled if an connection is established and no subscription set
            this.TextBox_Group.Enabled = ((newState >= InternalStateControl.Connected) &&
                (newState < InternalStateControl.SubscriptionSet));

            //enabled if an connection is established and until no item added
            state = ((newState >= InternalStateControl.Connected) &&
                (newState < InternalStateControl.ItemsSet));
            this.Button_Add_Remove_Group.Enabled = state;
            if (newState < InternalStateControl.SubscriptionSet)
                this.Button_Add_Remove_Group.Text = BUTTON_DESCRIPTION_GROUP_ADD;
            else
                this.Button_Add_Remove_Group.Text = BUTTON_DESCRIPTION_GROUP_REMOVE;

            //enabled if an subscription is added
            state = (newState >= InternalStateControl.SubscriptionSet);
            this.Button_GroupInfo.Enabled = state;

            //enabled if an subscription is added
            state = (newState >= InternalStateControl.SubscriptionSet);
            this.Button_Add_Item.Enabled = state;

            //enabled if items are added
            state = (newState >= InternalStateControl.ItemsSet);
            this.Button_Remove_Item.Enabled = state;

            //enable or disable item data controls
            SetInternalStateItemData();
        }

        private void SetInternalStateItemData()
        {
            Boolean state;

            //enabled if items are added
            state = (this.m_InternalStateControl >= InternalStateControl.ItemsSet);
            this.RadioButton_Sync.Enabled = state;
            this.RadioButton_Async.Enabled = state;
            this.RadioButton_Cyclic.Enabled = state;
            this.ListView_ItemData.Enabled = state;

            //enabled if access state sync or async active
            state = ((this.m_InternalStateControl >= InternalStateControl.ItemsSet) &&
                (this.RadioButton_Cyclic.Checked == false));
            this.Button_Read.Enabled = state;
            this.Button_Write.Enabled = state;
            this.Label_ItemValue.Enabled = state;
            this.TextBox_ItemValue.Enabled = state;
        }

        private void SetReadWriteState(Boolean enabled)
        {
            // enable or disable controls
            this.Button_Read.Enabled = enabled;
            this.Button_Write.Enabled = enabled;
            this.Label_ItemValue.Enabled = enabled;
            this.TextBox_ItemValue.Enabled = enabled;
        }

        private void ShowExceptionInfo(Exception except)
        {
            StringBuilder sbText, sbCaption;
            sbText = new StringBuilder(except.Message);
            sbCaption = new StringBuilder("Exception caugth in ");
            sbCaption.Append(this.Text);
            MessageBox.Show(sbText.ToString(), sbCaption.ToString(),
                MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
        }

        private void ComboBox_Server_DropDown(object sender, EventArgs e)
        {

        }

        //private void ServerShutdownHandler ( String reason )
        //{
        //   MessageBox.Show ( "OPC Server shutdown in case of " + reason, 
        //      MsgBoxStyle.Exclamation | MsgBoxStyle.OkOnly, 
        //      this.Text );
        //}

        #endregion

        private void button1_Click(object sender, EventArgs e)
        {
            MessageBox.Show("q123123123");
        }
    }
}
